#!/usr/bin/env python
######################################################################
##
## Copyright (C) 2008,  Simon Kagstrom
##
## Filename:      shcov
## Author:        Simon Kagstrom <simon.kagstrom@gmail.com>
## Description:   Test shell script coverage
##
## $Id:$
##
######################################################################

# export PS4='SHCOV:::${BASH_SOURCE}:::${LINENO}::: SHCOV:'

import os, sys, subprocess, pickle, signal, getopt

sys.path.append(os.path.abspath(sys.path[0] + "/../"))
import shcov

from shcov.file import File

class ShcovCollector:
    def __init__(self, args, outpath = "/tmp/shcov", shell = ["bash", "-x"]):
	self.files = {}
	self.args = args
	self.outpath = outpath
	self.shell = shell

	# Where we execute this script
	os.environ["PS4"] = "SHCOV:::${BASH_SOURCE}:::${LINENO}::: SHCOV:"

	self.process = None

    def handle_line(self, line):
	parts = line.split(":::")

	file_name = parts[1]
	line_nr = parts[2]

	# Get the file for this path
	path = os.path.abspath(file_name)
	try:
	    file = self.files[path]
	except KeyError, e:
	    # Not found, try to load it or otherwise create a new one
	    try:
		file = shcov.file.load(self.outpath + path + ".pkl")
	    except Exception, e:
		# Create a new one (no such file)
		file = File(path)
            # And create the dir to save it in
	    try:
		os.makedirs( os.path.dirname(self.outpath + file.path) )
	    except OSError, e:
		pass
	    self.files[path] = file
	file.add_to_line(line_nr)
        file.save(self.outpath + file.path + ".pkl")

    def run(self):
	self.process = subprocess.Popen( self.shell + self.args,  stdin = sys.stdin,
					 stdout = sys.stdout, stderr = subprocess.PIPE )

	ret = None
	while True:
	    # Write out everything on stderr
	    self.process.stderr.flush()

	    line = self.process.stderr.readline()
	    if line.find("SHCOV:::") != -1:
		self.handle_line(line)
	    else:
		sys.stdout.write(line)

	    # This looks stupid, but we want one more read to get
	    # the last output when the process exits
	    if ret != None:
		return ret
	    ret = self.process.poll()

    def save(self):
	for file in self.files.values():
	    file.save(self.outpath + file.path + ".pkl")

def usage():
    print "Usage: shcov [-h] [--output=where] [--shell=what] script...\n"
    print "Produce coverage data for 'script'. Options are\n"
    print "  --output=where  write data to 'where' instead of /tmp/shcov"
    print "  --shell=what    ues 'what' (including arguments) as shell instead of 'bash -x'"

    sys.exit(1)

glob_sc = None

def sighandler(signum, frame):
    glob_sc.save()
    sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) < 2:
	usage()

    outpath = "/tmp/shcov"
    shell = ["bash", "-x"]

    optlist, args = getopt.gnu_getopt(sys.argv[1:], "ho:s:", ["help", "output=", "shell="])
    for opt, arg in optlist:
	if opt in ("-h", "--help"):
	    usage()
	if opt in ("-o", "--output"):
	    outpath = arg
	if opt in ("-s", "--shell"):
	    shell = arg.split()

    if len(args) < 1:
	usage()

    sc = ShcovCollector(args, outpath = outpath, shell = shell)
    glob_sc = sc
    signal.signal(signal.SIGTERM, sighandler)
    out = 1
    try:
	out = sc.run()
    except KeyboardInterrupt, e:
	# Just save
	pass
    sc.save()
    sys.exit(out)
